#include <stdio.h>
#include <vector>
#include <iostream>
#include <algorithm>

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#include "logger.hpp"
#include "mapped_file.hpp"

const int SIZE_OF_INT = sizeof(int);

bool WriteIntToMappedFile(MappedFile& array, long position, int num) {
    for(int j = 0; j < SIZE_OF_INT; j++) {
        array[position + j] = ((char*)(&num))[j];
    }
    return true;
}

int ReadIntFromMappedFile(MappedFile& array, long position) {
    int num;
    for(int j = 0; j < SIZE_OF_INT; j++) {
        ((char*)(&num))[j] = array[position + j];
    }
    return num;
}


bool MergeSort(MappedFile& input, MappedFile& auxiliary, long begin_to_sort, long end_to_sort) {  
    if((end_to_sort - begin_to_sort) < 2) return true;
    long medium = begin_to_sort + (end_to_sort - begin_to_sort) / 2;

    MergeSort(input, auxiliary, begin_to_sort, medium);
    MergeSort(input, auxiliary, medium, end_to_sort);
    
    //int cur1 = begin_to_sort;
    //int cur2 = medium;
    //cur1 is current position in first part of array which we want to merge
    int cur1 = begin_to_sort;

    //cur2 is current position in second part of array which we want to merge
    int cur2 = medium;
    //int outcur = begin_to_sort;
    //outcur is current position in array where we would merge to
    int outcur = begin_to_sort;
    while(cur1 < medium && cur2 < end_to_sort) {
        int num1 = ReadIntFromMappedFile(input, cur1
            * SIZE_OF_INT);
        int num2 = ReadIntFromMappedFile(input, cur2
            * SIZE_OF_INT);
    
        if(num1 < num2) {
            WriteIntToMappedFile(auxiliary, outcur * SIZE_OF_INT,
                num1);
            cur1++;
        } else {
            WriteIntToMappedFile(auxiliary, outcur * SIZE_OF_INT,
                num2);
            cur2++;
        }
        outcur++;
        
    }
    
    while(cur1 < medium) {
        int num1 = ReadIntFromMappedFile(input, cur1
            * SIZE_OF_INT);
        WriteIntToMappedFile(auxiliary, outcur * SIZE_OF_INT, num1);
        cur1++;
        outcur++;
        //std::cout << num_from_first_part << " ";
    }
    while(cur2 < end_to_sort) {
        int num2 = ReadIntFromMappedFile(input, cur2
            * SIZE_OF_INT);
        WriteIntToMappedFile(auxiliary, outcur * SIZE_OF_INT, num2);
        cur2++;
        outcur++;
        //std::cout << num_from_second_part << " ";
    }

    for(int i = begin_to_sort; i < end_to_sort; i++) {
        int num = ReadIntFromMappedFile(auxiliary, i * SIZE_OF_INT);
        WriteIntToMappedFile(input, i * SIZE_OF_INT, num);
    }
}
            

        
    

bool Sort(MappedFile& input, MappedFile& output) {
    MappedFile first_array;
    MappedFile second_array;
    if(!first_array.Initialize("first_array.txt", MappedFile::WRITE)) {
        LogEr("can't initialize Mapped file(first_array.txt)");
        return false;
    }
    if(!second_array.Initialize("second_array.txt", MappedFile::WRITE)) {
        LogEr("can't initialize Mapped file(second_array.txt)");
        return false;
    }

    long input_size = input.GetSizeOfFile();
    char ch;
    std::string number;   
    long quantity_of_numbers = 0;
    char* end_ptr;

    for(int i = 0; i < input_size; i++) {
        ch = input[i];
        if(isdigit(ch)) {
            number.push_back(ch);
            continue;
        }
        if(number.size() == 0) continue;
        if(number.size() > 10) {
            LogEr("Too big numbers");
            return false;
        }
        int num = strtol(number.c_str(), &end_ptr, 10);
        number.clear();
        if(*end_ptr != '\0' || errno == ERANGE) {
            LogEr("Incorect array");
            return false;
        }
        
        WriteIntToMappedFile(first_array, quantity_of_numbers * SIZE_OF_INT, num);       
        quantity_of_numbers++;
    }
   
    MergeSort(first_array, second_array, 0, quantity_of_numbers);

    std::cout << "work\n";
    //we write int "as string" into this buffer to discover his(num) length;
    char buffer[15];
    int current_position_in_array = 0;
    for(int i = 0; i < quantity_of_numbers; i++) {
        int num = ReadIntFromMappedFile(first_array, i * SIZE_OF_INT);
        sprintf(buffer, "%d,\0", num);
        int len = strlen(buffer);
        for(int j = 0; j < len; j++) {
            output[current_position_in_array + j] = buffer[j];//crashed here
        }
        current_position_in_array += len;    
    }
    
    first_array.Finalize();
    second_array.Finalize();
    remove("first_array.txt");
    remove("second_array.txt");
        
    return true;
    
}


int main(int argc, char** argv) {
    if(argc != 3) return -1;
    if(argv[1] == argv[2]) return -1;
    Logger::Instance().Initialize("Log.txt");
    
    MappedFile input;
    if(!input.Initialize(argv[1], MappedFile::READ)) {
        LogEr("can't initialize Mapped file(argv[1])");
        return -1;
    }
    MappedFile output;
    if(!output.Initialize(argv[2], MappedFile::WRITE)) {
        LogEr("can't initialize Mapped file(argv[2])");
        return -1;
    }


    if(!Sort(input, output)) return -1;

    if(!input.Finalize() && !output.Finalize()) return -1;

   
    return 0;
}

